/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sigl.updater;

//~--- non-JDK imports --------------------------------------------------------
import sigl.updater.parsers.WebParser;
import sigl.updater.parsers.FileParser;
import sigl.updater.modelupdater.ModuleUpdater;
import sigl.updater.modelupdater.PackageUpdater;
import com.developpez.adiguba.shell.Shell;

import com.enterprisedt.net.ftp.FTPConnectMode;
import com.enterprisedt.net.ftp.FTPConnectionClosedException;
import com.enterprisedt.net.ftp.FileTransferClient;

import sigl.model.Model;

//~--- JDK imports ------------------------------------------------------------

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;

import java.net.URL;
import java.net.URLConnection;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 *
 * @author uxmal
 */
public class Updater {

    String cacheDirectory;
    Pattern ext_mask, archive_mask, file_mask, download_list_mask;
    ModuleUpdater modules;
    PackageUpdater packages;

    public Updater(Model model, String cacheDirectory) {
        modules = new ModuleUpdater();
        packages = new PackageUpdater();
        ext_mask = Pattern.compile("^([a-z0-9]+)\\.pm$", Pattern.CASE_INSENSITIVE);
        archive_mask = Pattern.compile("^([a-z\\-_.0-9]+)\\.(?:tar\\.gz|tgz|zip)$", Pattern.CASE_INSENSITIVE);
        file_mask = Pattern.compile("^.*/(.*)\\.(tar\\.gz|tgz|zip) *$", Pattern.CASE_INSENSITIVE);
        download_list_mask = Pattern.compile("^(.+) +[a-z0-9_\\.\\-\\+]+  +(.+) *$", Pattern.CASE_INSENSITIVE);
        this.cacheDirectory = cacheDirectory;
        modules = new ModuleUpdater(model.getModules());
        packages = new PackageUpdater(model.getPackages());
    }

    public Model getModel() {
        Model model = new Model();

        model.setModules(modules.getModules());
        model.setPackages(packages.getPackages());

        return model;
    }

    public void updateFromFile(String updateFile) {
        HashMap fileList = loadUpdateFile(updateFile);

        // itérateur
        Set<Map.Entry<String, String>> set = fileList.entrySet();
        FileTransferClient ftp = null;

        try {

            ftp = new FileTransferClient();
            ftp.getAdvancedFTPSettings().setConnectMode(FTPConnectMode.PASV);

            ftp.setRemoteHost("ftp.easynet.be");
            ftp.setUserName("anonymous");
            ftp.setPassword("astropionner@gmail.com");
            ftp.connect();

        } catch (Exception e) {
            System.out.println("connection au serveur FTP impossible !");
            e.printStackTrace();
            return;
        }
        System.out.println("connecté au serveur FTP.");
        // pour chaque package
        for (Map.Entry<String, String> entry : set) {
            String moduleName = entry.getKey();
            String packagePath = entry.getValue();

            String fileName = "";
            String folderName = "";
            String archiveExtension = "";

            try {
                Matcher m = file_mask.matcher(packagePath);

                if ((m.matches()) && (m.groupCount() > 1)) {
                    folderName = m.group(1);
                    archiveExtension = m.group(2);
                    fileName = folderName + "." + archiveExtension;
                } else {
                    System.out.println("Erreur de lecture sur " + packagePath);
                    continue;
                }
            } catch (PatternSyntaxException pse) {
            }

            // maj
            packages.addToPackage(folderName, moduleName);
            modules.setPackageName(moduleName, folderName);
                
            if (!packages.hasBeenParsed(folderName)) {

                //sinon, on doit ajouter le module au package, et inversement

                //System.out.println("On a déjà le package "+folderName);
                continue;
            }
           //System.out.println("On doit analyser le package "+folderName);


            System.out.println(folderName + ":");
            System.out.println("  packagePath: " + packagePath);



            int lower = 5000;
            int higher = 8000;  // entre 5 et 9 sec

            int random = (int) (Math.random() * (higher - lower)) + lower;

            try {
                Thread.sleep(random);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }



            // on récupère les métas données associées au package depuis le site de CPAN
            System.out.println("  téléchargement des méta données...");
            System.out.flush();

            WebParser wp = new WebParser(folderName);
            packages.setPackagePass(folderName, wp.getPassed());
            packages.setPackageFail(folderName, wp.getFailed());

            String fileString = cacheDirectory + fileName;
            String folderString = cacheDirectory + folderName;
            File archive = new File(fileString);
            File folder = new File(folderString);

            if (!folder.exists()) {

                if (!archive.exists()) {
                    System.out.println("  téléchargement du code source..");
                    System.out.flush();

                    try {
                        ftp.downloadFile(fileString, "pub/CPAN/authors/id/" + packagePath);
                    } catch (FTPConnectionClosedException timeout) {
                        System.out.println("\n-> Reconnection au serveur FTP...\n");
                        try {
                            ftp.connect();
                            ftp.downloadFile(fileString, "pub/CPAN/authors/id/" + packagePath);
                        } catch (Exception ex) {
                            System.out.println("\n/!\\ impossible de se reconnecter !...\n");
                            System.out.println(ex);
                            System.exit(1);
                        }
                    } catch (Exception e) {
                        System.out.println("  téléchargement impossible.");
                        System.out.println(e);
                    // System.exit(1);

                    }
                }

                archive = new File(fileString);

                if (archive.exists()) {
                    Shell sh = new Shell();

                    try {
                        System.out.println("  décompression...");

                        if (archiveExtension.equalsIgnoreCase("tgz") || archiveExtension.equalsIgnoreCase("tar.gz")) {

                            // System.out.println("cd " + cacheDirectory + " && tar xvzf " + fileName);
                            sh.command("cd " + cacheDirectory + " && tar xvzf " + fileName).consumeAsString();
                        } else if (archiveExtension.equalsIgnoreCase("zip")) {

                            // System.out.println("cd " + cacheDirectory + " && unzip " + fileName + " -d " + folderName);
                            sh.command("cd " + cacheDirectory + " && unzip " + fileName + " -d " + folderName).consumeAsString();
                        } else {
                            System.out.println("  format d'archive " + archiveExtension + " non géré.");
                        }
                    } catch (java.io.IOException exc) {
                    }
                } else {

                    // System.out.println("téléchargement de " + packagePath + " échoué.");
                }
            }

            if (folder.exists()) {
                System.out.println("  analyse du code source...");
                parseEntry(folder);
            } else {
                System.out.println("  impossible d'analyser le code source.");
                System.out.println();
            }
        }

        try {
            // Shut down client
            ftp.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    void parseCache() {
        parseEntry(new File(cacheDirectory));
    }

    private HashMap loadUpdateFile(String fileName) {
        BufferedReader reader = null;
        String line;
        HashMap fileList = new HashMap();

        try {
            reader = new BufferedReader(new FileReader(fileName));
        } catch (FileNotFoundException exc) {
            System.out.println("Erreur d'ouverture");
        }

        try {
            while ((line = reader.readLine()) != null) {
                Matcher m = download_list_mask.matcher(line);

                if ((m.matches()) && (m.groupCount() > 1)) {
                    fileList.put(m.group(1).trim(), m.group(2).trim());
                } else {
                    System.out.println("impossible de lire la ligne " + line);
                }
            }

            reader.close();
        } catch (PatternSyntaxException pse) {    /* regex correcte */

        } catch (IOException exc) {
            System.out.println("Erreur de lecture !");
        }

        return fileList;
    }

    private void downloadFile(String urlString, String fileString)
            throws java.net.MalformedURLException, java.io.IOException {
        URL url = null;

        url = new URL(urlString);

        // URL url = new URL("ftp://username:password@ftp.whatever.com/file.zip;type=i");
        URLConnection con = null;

        con = url.openConnection();

        BufferedInputStream in = null;
        FileOutputStream out = null;

        in = new BufferedInputStream(con.getInputStream());
        out = new FileOutputStream(fileString);

        int i = 0;
        byte[] bytesIn = new byte[1024];

        while ((i = in.read(bytesIn)) >= 0) {
            out.write(bytesIn, 0, i);
        }

        out.close();
        in.close();
    }

    private void parseEntry(File entry) {
        if (entry.isDirectory()) {

            // on ignore le répertoire des tests
            if (entry.getName().matches("^/?t/?$")) {
                return;
            }
            File[] new_entries = entry.listFiles();

            // on ignore silencieusement les erreurs filesystem (répertoire protégé ou corrompu etc...)
            if (new_entries == null) {
                return;
            } // retourne null si listFiles() impossible

            for (int i = 0; i < new_entries.length; i++) {
                if (new_entries[i].isFile()) {
                    try {
                        Matcher m = archive_mask.matcher(new_entries[i].getName());
                        if ((m.matches()) && (m.groupCount() > 0)) {
                            // System.out.println("   -> Décompression de "+new_entries[i].getName());
                        }
                    } catch (PatternSyntaxException pse) {    /* regex correcte */

                    }
                }
            }

            // nouvelle recherche : les sous répertoires
            File[] entries = entry.listFiles();

            if (entries == null) {
                return;
            } // retourne null si listFiles() impossible

            // pour chaque entrée du folder
            for (int i = 0; i < entries.length; i++) {
                parseEntry(entries[i]);
            }
        } else if (entry.isFile()) {
            try {                                     // on regarde si le fichier est un .pm

                Matcher m = ext_mask.matcher(entry.getName());

                if ((m.matches()) && (m.groupCount() > 0)) {
                    parseModule(entry);
                }                                     // TODO non absolute ? remplacer par le File ?

            } catch (PatternSyntaxException pse) {    /* regex correcte */

            }
        }
    }

    private void parseModule(File file) {
        FileParser fp = new FileParser(file);

        // CHEMIN et OM DU MODULE
        String filePath = file.getAbsolutePath();
        String moduleName = fp.getModuleName();

        // DEPENDANCES
        modules.setDependencies(moduleName, fp.getDependencies());
    }
}
//~ Formatted by Jindent --- http://www.jindent.com
